// $Id$
package de.servicezombie.pub.r1;

import java.lang.reflect.InvocationTargetException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.servicezombie.lang.ErrorCode;
import de.servicezombie.lang.ErrorCodeAware;
import de.servicezombie.lang.WrappedException;

public class ExceptionForWebResponseTransformerDefaultImpl implements ExceptionForWebResponseTransformer {
	
	private static final Logger log = LoggerFactory.getLogger(ExceptionForWebResponseTransformerDefaultImpl.class);
	
	private Map<Class<? extends Throwable>, ErrorCode> exceptionMappings;
	private Set<Class<? extends Throwable>> exceptionsWithCauseMappings;
	
	public ExceptionForWebResponseTransformerDefaultImpl() {
		
		exceptionsWithCauseMappings = new HashSet<Class<? extends Throwable>>();
		exceptionsWithCauseMappings.add(InvocationTargetException.class);
		exceptionsWithCauseMappings.add(WrappedException.class);
		
		exceptionMappings = new HashMap<Class<? extends Throwable>, ErrorCode>();
		exceptionMappings.put(IllegalArgumentException.class, ErrorCode.ILLEGAL_ARGUMENT);
		
	}
	
	public void setExceptionMappings(Map<Class<? extends Throwable>, ErrorCode> exceptionMappings) {
		this.exceptionMappings = exceptionMappings;
	}
	
	public void setExceptionsWithCauseMappings(Set<Class<? extends Throwable>> exceptionsWithCauseMappings) {
		this.exceptionsWithCauseMappings = exceptionsWithCauseMappings;
	}

	/* (non-Javadoc)
	 * @see de.servicezombie.pub.r1.ExceptionToReturnCodeTransformer#transform(java.lang.Throwable)
	 */
	@Override
	public final ErrorCode transformToErrorCode(Throwable exception) {
		
		if(exception == null) {
			return ErrorCode.OK;
		}
		
		exception = findMostInterestingException(exception);

		if(exception instanceof ErrorCodeAware) {
			ErrorCodeAware wec = (ErrorCodeAware) exception;
			return wec.getErrorCode();
		}
		
		if(exceptionMappings != null && exceptionMappings.containsKey(exception.getClass())) {
			return exceptionMappings.get(exception.getClass());
		}
		
		return ErrorCode.UNSPECIFIED;
	}

	@Override
	public final String transformToMessage(Throwable exception) {
		
		if(exception == null) {
			return StringUtils.EMPTY;
		}
		
		InetAddress localhost = null;
		String hostName = null;
		
		try {
			localhost = InetAddress.getLocalHost();
			hostName = localhost.getHostName().split("\\.")[0];
		}
		catch (UnknownHostException e) {
			log.error("cannot set local hostname.", e);
			try {
				localhost = InetAddress.getByName("localhost");
				hostName = localhost.getHostName();
			}
			catch (UnknownHostException ee) {
				log.error("localhost is unknown. this is really strange.", ee);
				hostName = "unknown-host";
			}
		}
		
		Date now = new Date();		
		return now + " " + hostName + " " + buildExceptionMessage(exception); 
		
	}
	
	/**
	 * @param throwable may be null
	 * @return message or empty string
	 */
	protected String buildExceptionMessage(Throwable throwable) {
				
		String message;
		
		Throwable exception = findMostInterestingException(throwable);
		message = exception.getClass().getSimpleName() + " : " + exception.getMessage();
		
		return message;
	}

	private Throwable findMostInterestingException(Throwable throwable) {
		
		if (exceptionsWithCauseMappings != null && exceptionsWithCauseMappings.contains(throwable.getClass())) {
			Throwable cause = throwable.getCause();
			if(cause != null) {
				return findMostInterestingException(cause);
			}
		}
		
		return throwable;
	}
}
